package com.kly.interview.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kly.interview.common.DeleteRequest;
import com.kly.interview.common.ErrorCode;
import com.kly.interview.constant.CommonConstant;
import com.kly.interview.exception.ThrowUtils;
import com.kly.interview.mapper.QuestionBankQuestionMapper;
import com.kly.interview.model.dto.questionbankquestion.QuestionBankQuestionBatchRequest;
import com.kly.interview.model.dto.questionbankquestion.QuestionBankQuestionQueryRequest;
import com.kly.interview.model.entity.Question;
import com.kly.interview.model.entity.QuestionBank;
import com.kly.interview.model.entity.QuestionBankQuestion;
import com.kly.interview.model.entity.User;
import com.kly.interview.model.vo.QuestionBankQuestionVO;
import com.kly.interview.model.vo.UserVO;
import com.kly.interview.service.QuestionBankQuestionService;
import com.kly.interview.service.QuestionBankService;
import com.kly.interview.service.QuestionService;
import com.kly.interview.service.UserService;
import com.kly.interview.utils.SqlUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 题库关联题目表服务实现
 *
 * @author <a href="https://github.com/liyupi">程序员鱼皮</a>
 * @from <a href="https://www.code-nav.cn">编程导航学习圈</a>
 */
@Service
@Slf4j
@Resource
public class QuestionBankQuestionServiceImpl extends ServiceImpl<QuestionBankQuestionMapper, QuestionBankQuestion> implements QuestionBankQuestionService {

    @Resource
    private UserService userService;

    @Lazy
    @Resource
    private QuestionService questionService;

    @Lazy
    @Resource
    private QuestionBankService questionBankService;

    /**
     * 校验数据
     *
     * @param questionBankQuestion
     * @param add                  对创建的数据进行校验
     */
    @Override
    public void validQuestionBankQuestion(QuestionBankQuestion questionBankQuestion, boolean add) {
        ThrowUtils.throwIf(questionBankQuestion == null, ErrorCode.PARAMS_ERROR);
        Long questionBankId = questionBankQuestion.getQuestionBankId();
        Long questionId = questionBankQuestion.getQuestionId();
        ThrowUtils.throwIf(questionBankId == null || questionBankId <= 0, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(questionId == null || questionId <= 0, ErrorCode.PARAMS_ERROR);

    }

    /**
     * 获取查询条件
     *
     * @param questionBankQuestionQueryRequest
     * @return
     */
    @Override
    public QueryWrapper<QuestionBankQuestion> getQueryWrapper(QuestionBankQuestionQueryRequest questionBankQuestionQueryRequest) {
        QueryWrapper<QuestionBankQuestion> queryWrapper = new QueryWrapper<>();
        if (questionBankQuestionQueryRequest == null) {
            return queryWrapper;
        }
        // todo 从对象中取值
        Long id = questionBankQuestionQueryRequest.getId();
        Long questionBankId = questionBankQuestionQueryRequest.getQuestionBankId();
        Long questionId = questionBankQuestionQueryRequest.getQuestionId();
        Long userId = questionBankQuestionQueryRequest.getUserId();
        Date createTime = questionBankQuestionQueryRequest.getCreateTime();
        Date updateTime = questionBankQuestionQueryRequest.getUpdateTime();
        int current = questionBankQuestionQueryRequest.getCurrent();
        int pageSize = questionBankQuestionQueryRequest.getPageSize();
        String sortField = questionBankQuestionQueryRequest.getSortField();
        String sortOrder = questionBankQuestionQueryRequest.getSortOrder();

        // todo 补充需要的查询条件
        // 从多字段中搜索
        // if (StringUtils.isNotBlank(searchText)) {
        //     // 需要拼接查询条件
        //     queryWrapper.and(qw -> qw.like("title", searchText).or().like("content", searchText));
        // }
        // 模糊查询

        // 精确查询
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionBankId), "questionBankId", questionBankId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(questionId), "questionId", questionId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId);
        // 排序规则
        queryWrapper.orderBy(SqlUtils.validSortField(sortField),
                sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    /**
     * 获取题库关联题目表封装
     *
     * @param questionBankQuestion
     * @param request
     * @return
     */
    @Override
    public QuestionBankQuestionVO getQuestionBankQuestionVO(QuestionBankQuestion questionBankQuestion, HttpServletRequest request) {
        // 对象转封装类
        QuestionBankQuestionVO questionBankQuestionVO = QuestionBankQuestionVO.objToVo(questionBankQuestion);

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Long userId = questionBankQuestion.getUserId();
        User user = null;
        if (userId != null && userId > 0) {
            user = userService.getById(userId);
        }
        UserVO userVO = userService.getUserVO(user);
        questionBankQuestionVO.setUser(userVO);

        // endregion

        return questionBankQuestionVO;
    }

    /**
     * 分页获取题库关联题目表封装
     *
     * @param questionBankQuestionPage
     * @param request
     * @return
     */
    @Override
    public Page<QuestionBankQuestionVO> getQuestionBankQuestionVOPage(Page<QuestionBankQuestion> questionBankQuestionPage, HttpServletRequest request) {
        List<QuestionBankQuestion> questionBankQuestionList = questionBankQuestionPage.getRecords();
        Page<QuestionBankQuestionVO> questionBankQuestionVOPage = new Page<>(questionBankQuestionPage.getCurrent(), questionBankQuestionPage.getSize(), questionBankQuestionPage.getTotal());
        if (CollUtil.isEmpty(questionBankQuestionList)) {
            return questionBankQuestionVOPage;
        }
        // 对象列表 => 封装对象列表
        List<QuestionBankQuestionVO> questionBankQuestionVOList = questionBankQuestionList.stream().map(questionBankQuestion -> {
            return QuestionBankQuestionVO.objToVo(questionBankQuestion);
        }).collect(Collectors.toList());

        // todo 可以根据需要为封装对象补充值，不需要的内容可以删除
        // region 可选
        // 1. 关联查询用户信息
        Set<Long> userIdSet = questionBankQuestionList.stream().map(QuestionBankQuestion::getUserId).collect(Collectors.toSet());
        Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream()
                .collect(Collectors.groupingBy(User::getId));

        // 填充信息
        questionBankQuestionVOList.forEach(questionBankQuestionVO -> {
            Long userId = questionBankQuestionVO.getUserId();
            User user = null;
            if (userIdUserListMap.containsKey(userId)) {
                user = userIdUserListMap.get(userId).get(0);
            }
            questionBankQuestionVO.setUser(userService.getUserVO(user));
        });
        // endregion

        questionBankQuestionVOPage.setRecords(questionBankQuestionVOList);
        return questionBankQuestionVOPage;
    }

    @Override
    public Boolean addQuestionBankQuestionBatch(QuestionBankQuestionBatchRequest questionBankQuestionBatchRequest) {
        // 校验参数

        ThrowUtils.throwIf(questionBankQuestionBatchRequest == null, ErrorCode.PARAMS_ERROR);
        Long questionBankId = questionBankQuestionBatchRequest.getQuestionBankId();
        List<Long> questionIds = questionBankQuestionBatchRequest.getQuestionIds();
        ThrowUtils.throwIf(questionBankId == null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIds), ErrorCode.PARAMS_ERROR);
        // 校验题库
        boolean questionBankExist = questionBankService.lambdaQuery()
                .eq(QuestionBank::getId, questionBankId)
                .exists();
        ThrowUtils.throwIf(!questionBankExist, ErrorCode.PARAMS_ERROR);
        // 查询已存在的题目id
        List<Long> existQuestionIds = this.lambdaQuery()
                .select(QuestionBankQuestion::getQuestionId)
                .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                .list()
                .stream()
                .map(QuestionBankQuestion::getQuestionId)
                .collect(Collectors.toList());
        // 筛选出不存在的题目
        List<Long> questionIdList = questionService.lambdaQuery()
                .select(Question::getId)
                .in(Question::getId, questionIds)
                .list()
                .stream()
                .map(Question::getId)
                .filter(id -> !existQuestionIds.contains(id))
                .collect(Collectors.toList());

        // 用户id
        long userId = StpUtil.getLoginIdAsLong();
        // 新建线程池
        List<List<Long>> splitSubList = splitList(questionIdList, DEFAULT_BATCH_SIZE);

        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
                10,
                20,
                60L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100),
                new ThreadPoolExecutor.AbortPolicy()
        );
        // 分批处理 避免长事务
        List<CompletableFuture<Void>> futureList = new ArrayList<>();
        // AOPContext.currentProxy()
        QuestionBankQuestionService questionBankQuestionService = (QuestionBankQuestionServiceImpl) AopContext.currentProxy();
        // 异步任务分批处理
        for (List<Long> subList : splitSubList) {
            futureList.add(CompletableFuture.runAsync(() -> {
                        // 添加
                        questionBankQuestionService.addQBQBatch(subList, questionBankId, userId);
                    }, poolExecutor).exceptionally(ex -> {
                        log.error("异步任务异常：{}", ex.getMessage());
                        return null;
                    })
            );
        }

        // 等待所有任务完成
        CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).join();

        // 关闭线程池
        poolExecutor.shutdown();
        return true;
    }

    private List<List<Long>> splitList(List<Long> list, int batchSize) {
        List<List<Long>> result = new ArrayList<>();
        int size = list.size();
        for (int i = 0; i < size; i += batchSize) {
            result.add(list.subList(i, Math.min(i + batchSize, size)));
        }
        return result;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addQBQBatch(List<Long> questionIdList, Long questionBankId, long userId) {
        if (CollUtil.isNotEmpty(questionIdList)) {
            //     批量添加
            List<QuestionBankQuestion> questionBankQuestionList = questionIdList.stream().map(question -> {
                QuestionBankQuestion questionBankQuestion = new QuestionBankQuestion();
                questionBankQuestion.setQuestionBankId(questionBankId);
                questionBankQuestion.setQuestionId(question);
                questionBankQuestion.setUserId(userId);
                return questionBankQuestion;
            }).collect(Collectors.toList());
            boolean b = this.saveBatch(questionBankQuestionList);
            ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
        }
    }

    @Override
    @Transactional
    public Boolean removeQuestionBankQuestionBatch(QuestionBankQuestionBatchRequest questionBankQuestionBatchRequest) {
        // 校验参数

        ThrowUtils.throwIf(questionBankQuestionBatchRequest == null, ErrorCode.PARAMS_ERROR);
        Long questionBankId = questionBankQuestionBatchRequest.getQuestionBankId();
        List<Long> questionIds = questionBankQuestionBatchRequest.getQuestionIds();
        ThrowUtils.throwIf(questionBankId == null, ErrorCode.PARAMS_ERROR);
        ThrowUtils.throwIf(CollUtil.isEmpty(questionIds), ErrorCode.PARAMS_ERROR);
        // 校验题库
        boolean questionBankExist = questionBankService.lambdaQuery()
                .eq(QuestionBank::getId, questionBankId)
                .exists();
        ThrowUtils.throwIf(!questionBankExist, ErrorCode.PARAMS_ERROR);

        if (CollUtil.isNotEmpty(questionIds)) {
            boolean remove = this.lambdaUpdate()
                    .eq(QuestionBankQuestion::getQuestionBankId, questionBankId)
                    .in(QuestionBankQuestion::getQuestionId, questionIds)
                    .remove();
            ThrowUtils.throwIf(!remove, ErrorCode.OPERATION_ERROR);
        }
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean removeQuestionBatch(DeleteRequest deleteRequest) {
        List<Long> ids = deleteRequest.getIds();
        ThrowUtils.throwIf(ids == null, ErrorCode.PARAMS_ERROR);
        for (Long id : ids) {
            boolean b = questionService.lambdaUpdate()
                    .eq(Question::getId, id)
                    .remove();
            ThrowUtils.throwIf(!b, ErrorCode.OPERATION_ERROR);
            //     删除题目表
            this.lambdaUpdate()
                    .eq(QuestionBankQuestion::getQuestionId, id)
                    .remove();
        }
        return true;
    }


}
